// Api及注册回调管理
import { error, log } from '@/utils/log-utils';
import CryptorUtils from "../utils/cryptor-utils";

const apiData = [
  { name: 'connect', serviceList: [] },
  { name: 'disconnect', serviceList: [] },
  { name: 'connect_error', serviceList: [] },
  { name: 'connect_timeout', serviceList: [] },
  { name: 'reconnect_attempt', serviceList: [] },
  { name: 'reconnect_error', serviceList: [] },
  { name: 'reconnect_failed', serviceList: [] },
  { name: 'reconnecting', serviceList: [] },
  { name: 'reconnect', serviceList: [] },
  // { name: 'ping', serviceList: [] },
  // { name: 'pong', serviceList: [] },

  // 全局异常错误提示
  { name: 'exceptionRespApi', serviceList: [] },
  // 房间异常错误提示
  { name: 'roomExceptionRespApi', serviceList: [] },
  // 对手加入房间返回
  { name: 'enemyJoinRoomRespApi', serviceList: [] },
  // 对手离开房间返回
  { name: 'enemyLeaveRoomRespApi', serviceList: [] },
  // 全局-有人加入房间
  { name: 'joinRoomRespApi', serviceList: [] },
  // 全局-有人离开房间
  { name: 'leaveRoomRespApi', serviceList: [] },
  // 全局-房间状态改变触发
  { name: 'roomStatusChangeRespApi', serviceList: [] },
  // 对局-对手移动棋子
  { name: 'moveChessRespApi', serviceList: [] },
  // 对局-同步房间数据返回
  { name: 'syncRoomDataRespApi', serviceList: [] },
  // 对局-同步对战数据返回
  { name: 'syncBattleDataRespApi', serviceList: [] },
  // 对局-对手发送聊天信息
  { name: 'chatRespApi', serviceList: [] },
  // 观战-加入观战返回
  { name: 'joinWatchRespApi', serviceList: [] },
  // 对局-游戏结束后返回
  { name: 'gameWinRespApi', serviceList: [] },
  // 对局-双方已准备并即将进入对局页面时返回
  { name: 'matchSuccessRespApi', serviceList: [] },
  // 服务器所有数据已保存，允许进入对战页面
  { name: 'allowInBattleApi', serviceList: [] },
  // 对局-有用户观战/离开房间
  { name: 'watchNotifyRespApi', serviceList: [] },
  // 系统-服务器通知客户端有数据需要恢复
  { name: 'sessionRecoverNotifyApi', serviceList: [] },
  // 对局-会话恢复结果通知
  { name: 'sessionRecoverRespApi', serviceList: [] },
  // 对局-对手离线信息提示
  { name: 'enemyOfflineRespApi', serviceList: [] },
  // 对局-账号登录冲突返回
  { name: 'userConflictRespApi', serviceList: [] },
  // 对局-对手上线信息监听
  { name: 'enemyOnlineRespApi', serviceList: [] },
  // 对局-我方发起求和
  { name: 'sendPeaceRespApi', serviceList: [] },
  // 对局-对手方处理了求和意愿
  { name: 'sendPeaceResultRespApi', serviceList: [] },
  // 对局-我方发起悔棋
  { name: 'backMoveRespApi', serviceList: [] },
  // 对局-对方处理了悔棋意愿
  { name: 'backMoveResultRespApi', serviceList: [] },
  // 对局-倒计时监听
  { name: 'userTimeRespApi', serviceList: [] },
  // 系统-检测版本
  { name: 'versionRespApi', serviceList: [] },
  // 系统-详细发行的版本内容
  { name: 'versionDetailApi', serviceList: [] },
  // 监听在线人数
  { name: 'onlineCountRespApi', serviceList: [] },
  // 监听对战场次数
  { name: 'battleCountRespApi', serviceList: [] },
  // 被房主请出房间
  { name: 'kickUserRespApi', serviceList: [] },
];

/**
 * socket事件管理
 */
class SocketEvent {

  /**
   * 注册所有要监听的事件
   */
  static startAllServiceLister = (socket) => {
    apiData.map(api => {
      socket.on(api.name, data => {
        api.serviceList.map(service => {
          eval(service.callback)(CryptorUtils.decrypt(data));
        });
      });
    });
    log(`监听事件已注册成功`);
  };

  /**
   * 注册回调监听
   * 备注：因为socket多次监听的话，也会响应多次，所以统一注册，以及动态回调
   * @param name  要注册的api名称
   * @param serviceId 要注册的服务id(此id是在卸载的时候使用)
   * @param callback  要注册的服务id所对应的回调函数
   * @return {boolean}
   */
  static register = (name, serviceId, callback) => {
    const apiObj = apiData.find(service => service.name === name);
    if (!apiObj) {
      // tips: 遇此错误请在本类的 apiData 中加入本ApiName.
      error(`需要注册的ApiName: ${name} 无法被找到，请进行注册`);
      return false;
    }
    // 寻找一下是否有重复的回调
    const index = apiObj.serviceList.findIndex(service => service.id === serviceId);
    if (index !== -1) {
      apiObj.serviceList.splice(index, 1, { id: serviceId, callback: callback });
    } else {
      apiObj.serviceList.push({ id: serviceId, callback: callback });
    }
    return true;
  };

  /**
   * 卸载已注册的回调
   * @param serviceList
   */
  static unRegister = serviceList => {
    apiData.map(api => {
      api.serviceList = api.serviceList.filter(service => {
        return !serviceList.some(serviceId => serviceId === service.id);
      });
    });
  };

  /**
   * 全局发送的方法
   * @param socket
   * @param api
   * @param params
   * @param fn
   */
  static emit = (socket, api, params, fn) => {
    log(`请求api：${api}, 参数：`, params);
    if(socket) {
      // 封装包
      const packParams = CryptorUtils.encrypt(params);
      // 发送消息
      socket.emit(api, packParams, resp => {
        let decryptData = null;
        try {
          decryptData = CryptorUtils.decrypt(resp);
        } catch (e) {
          error(`Api: ${api}传回的消息错误，消息体为：`, resp, e);
        }
        fn && fn(decryptData);
      });
    } else {
      fn({ code: 'fail', msg: `您已离线，请稍候重试${api}` });
    }

    // 默认情况下，socket会在断开的时间内将已发生的事件缓存
    // 使用 volatile 可以忽略此种行为
    // 参考文献: https://socket.io/docs/v3/client-offline-behavior/
    // if(socket.connected) {
      // 发送消息
      // socket.emit(api, params, resp => {
      //   if(resp instanceof Object) {
      //     fn && fn(resp);
      //   } else {
      //     error(`Api: ${api}传回的消息非对象，消息体为：`, resp);
      //   }
      // });
    // } else {
    //   fn({ code: 'fail', msg: `您处于离线状态，请重试${api}` });
    // }
  };
}

export default SocketEvent;
